(function($, gmwk) {

    function Router(Options) {
    
      var self = this;
      var sClassName = Options.sClassName;
      var aHistory = [];
      var nHistoryLength = 2;  // This does not affect browser back button behaviour
      var sControlParam = "l";
      var Controller; // Do not give this a value, must be undefined initially
      var Privileged = { // These layouts require authenticated users
        Dashboard: true
      };
      
      // Public constants
      this.PARAMS_OVERRIDE_STATE = 0;
      this.STATE_OVERRIDE_PARAMS = 1;
      this.PARAMS_REPLACE_STATE = 2;
      
      $(window).bind(
          "hashchange",
          function(e) {
              // console.info("hashchange", e.fragment)
              fHashChange(e.fragment);
          }
      )
          
      // Initialize empty previous fragment
      EmptyFragment = {};
      EmptyFragment[sControlParam] = "";
      fRememberURLFragment(EmptyFragment);
      
      // console.info("---------> Router constructed");
      
      this.setControlParam = function(sMyControlParam) {
          sControlParam = sMyControlParam;
      }
  
      this.route = function() {
          var CurrentFragment = $.deparam.fragment(true);
          // Do not route if sControlParam missing or class undefined
          if (
              (CurrentFragment[sControlParam] === "") || 
              (CurrentFragment[sControlParam] === undefined) ||
              (gmwk.classes[CurrentFragment[sControlParam]] === undefined)
          ) {
              return false;
          }
          self.trigger();
          return true;
      }
  
      this.load = function(Options, nMode) {
          if (nMode === undefined) {
              nMode = self.PARAMS_OVERRIDE_STATE;
          }
          fSetURLFragment(Options, nMode);
          // Manually trigger hashchange to handle page reload
          if (typeof Controller == "undefined") {
              self.trigger();
          }
      };
      
      this.notFound = function() {
          fNotFound();
      }
      
      this.trigger = function() {
          $(window).trigger('hashchange');
      }
      
      function fRememberURLFragment(Fragment) {
          // Add current URL to the history
          aHistory.push(Fragment);
          if (aHistory.length > nHistoryLength) {
              // Trim the stack
              aHistory.splice(0, 1);
          }
      }
  
      function fGetURLFragment() {
          return $.deparam.fragment(true);
      }
      
      function fSetURLFragment(Options, nMode) {
          $.bbq.pushState(Options, nMode);
      }
      
      function fHashChange(sFragment) {
          console.info("---------> fHashChange", aHistory);
          var CurrentFragment = $.deparam(sFragment, true);
          var PreviousFragment = aHistory[aHistory.length - 1];
          fRememberURLFragment(CurrentFragment);
          // Do not route if sControlParam missing
          if (CurrentFragment[sControlParam] == "" || CurrentFragment[sControlParam] === undefined) {
              return;
          }
          // Show the user where he is, also makes back button more useable
          document.title = "GMWK " + CurrentFragment[sControlParam];
          //console.info(CurrentFragment[sControlParam], "bAuthorized", gmwk.bAuthorized, "Privileged", Privileged[CurrentFragment[sControlParam]]);
          // Check credentials for privileged layouts
          if (
              (Privileged[CurrentFragment[sControlParam]] !== undefined) &&
              !gmwk.bAuthorized
          ) {
              console.info("redirect");
              // Ask for credentials
              self.load(
                  {
                      l: "Login",
                      r: CurrentFragment[sControlParam]
                  }
              );
              return;
          // Fragment has changed
          } else if (!gmwk.compareObjects(PreviousFragment, CurrentFragment)) {
              //console.info("fragment changed", CurrentFragment[sControlParam]);
              //console.info("---> fragment changed");
              var bControlParamChanged = (PreviousFragment[sControlParam] != CurrentFragment[sControlParam]);
              // TODO: Calls gmwk.fLoadClass before doing this check,
              // otherwise all classes used a layout controllers must be included in index.html
              var bControllerClassDefined = (gmwk.classes[CurrentFragment[sControlParam]] !== undefined)
              var bControllerValid = false;
              if (bControllerClassDefined) {
                  bControllerValid = (Controller instanceof gmwk.classes[CurrentFragment[sControlParam]]);
              }
              /*
              console.info("bControlParamChanged", bControlParamChanged);
              console.info("bControllerClassDefined", bControllerClassDefined);        
              console.info("bControllerValid", bControllerValid);
              */
              // Create a new controller
              if ((bControlParamChanged || !bControllerValid) && bControllerClassDefined) {
                  /* console.info("---> new controller"); */
                  gmwk.clearStage();
                  Controller = gmwk.create(
                      CurrentFragment[sControlParam],
                      {
                          sTemplate: CurrentFragment[sControlParam],
                          jqParentContainer: gmwk.getStageContainer()
                      }
                  );
              }
              // Controller class is not defined
              if (!bControllerClassDefined) {
                  /* console.info("---> not found"); */
                  fNotFound();
              }
              // Call Controller.route if it exists
              if (
                (typeof Controller !== "undefined") &&
                (Controller.route !== undefined)
              ) {
                  //console.info("Controller.route");
                  Controller.route(CurrentFragment);
              };
          }
      }
      
      function fNotFound() {
        // TODO: Create not found class
        gmwk.clearStage();
        gmwk.getStageContainer()
          .html("<div class='global_NotFound_content'>The specified URL was not found.</div>");
      }
  
    }
    
    gmwk.classes.Router = Router;

})(jQuery, window.gmwk);
